Programmeringsspråkböcker förklarar att värdetyper skapas på stacken och referenstyper skapas på högen utan att förklara vad dessa två saker är. Jag har inte läst en tydlig förklaring av detta. Jag förstår vad en stack är. Men, Var och vad är de (fysiskt i en riktig dators minne)? I vilken utsträckning styrs de av operativsystemet eller språktid? Vad är deras omfattning? Vad avgör storleken på var och en av dem? Vad gör en snabbare?
2020-12-07 21:41:52
Stapeln är minnet avsatt som skraputrymme för en tråd för körning. När en funktion anropas reserveras ett block högst upp på stacken för lokala variabler och lite bokföringsdata. När den funktionen återvänder blir blocket oanvänt och kan användas nästa gång en funktion anropas. Stapeln är alltid reserverad i en LIFO-ordning (sist i först ut); det senast reserverade blocket är alltid nästa block som ska frigöras. Detta gör det väldigt enkelt att hålla koll på stacken; att frigöra ett block från stacken är inget annat än att justera en pekare. Heapen är minne avsatt för dynamisk allokering. Till skillnad från stacken finns det inget påtvingat mönster för allokering och deallocation av block från högen; du kan tilldela ett block när som helst och frigöra det när som helst. Detta gör det mycket mer komplicerat att hålla reda på vilka delar av högen som tilldelas eller är gratis vid varje given tidpunkt. det finns många anpassade högtilldelare tillgängliga för att ställa in högprestanda för olika användningsmönster. Varje tråd får en stack, medan det vanligtvis bara finns en hög för applikationen (även om det inte är ovanligt att ha flera högar för olika typer av tilldelning). För att svara på dina frågor direkt: I vilken utsträckning styrs de av operativsystemet eller språktiden? OS tilldelar stacken för varje tråd på systemnivå när tråden skapas. Vanligtvis kallas operativsystemet av språktiden för att allokera högen för applikationen. Vad är deras omfattning? Stapeln är fäst vid en tråd, så när tråden går ut återställs stacken. Heapen tilldelas vanligtvis vid körning av applikationen och körs tillbaka när applikationen (teknisk process) avslutas. Vad avgör storleken på var och en av dem? Storleken på stacken ställs in när en tråd skapas. Storleken på högen ställs in vid applikationsstart, men kan växa när det behövs utrymme (allokeraren begär mer minne från operativsystemet). Vad gör en snabbare? Stapeln är snabbare eftersom åtkomstmönstret gör det trivialt att allokera och distribuera minne från det (en pekare / heltal ökas helt enkelt eller minskas), medan högen har mycket mer komplex bokföring involverad i en allokering eller deallocation. Dessutom tenderar varje byte i stacken att återanvändas mycket ofta, vilket innebär att den tenderar att mappas till processorns cache, vilket gör den väldigt snabb. En annan prestationshit för heapen är att heapen, som mestadels är en global resurs, vanligtvis måste vara multitrådssäker, dvs. varje allokering och deallocation måste - vanligtvis - synkroniseras med "alla" andra heapaccesser i programmet. En tydlig demonstration: Bildkälla: vikashazrati.wordpress.com | Stack: Lagras i datorns RAM precis som högen. Variabler som skapas i stacken kommer att räcka ut och omlokaliseras automatiskt. Mycket snabbare att fördela jämfört med variabler på högen. Implementerad med en faktisk stapeldatastruktur. Lagrar lokal data, returadresser, används för parameteröverföring. Kan ha ett stacköverflöde när för mycket av stacken används (mestadels från oändlig eller för djup rekursion, mycket stora tilldelningar). Data som skapats på stacken kan användas utan pekare. Du skulle använda stacken om du vet exakt hur mycket data du behöver allokera innan kompileringstiden och den inte är för stor. Har vanligtvis en maximal storlek redan bestämd när ditt program startar. Högen: Lagras i datorns RAM precis som stacken. I C ++ måste variabler på högen förstöras manuellt och aldrig falla utanför räckvidden. Uppgifterna frigörs med radering, radering [] eller gratis. Långsammare att fördela jämfört med variabler i stacken. Används vid behov för att allokera ett datablock för användning av programmet. Kan ha fragmentering när det finns många tilldelningar och deallokationer. I C ++ eller C kommer data som skapas på högen pekas på av pekare och allokeras med nya respektive malloc. Kan ha allokeringsfel om för stor buffert begärs att tilldelas. Du skulle använda högen om du inte vet exakt hur mycket data du behöver vid körning eller om du behöver allokera mycket data. Ansvarig för minnesläckor. Exempel: int foo () { char * pBuffer; // <- inget tilldelat ännu (exklusive själva pekaren, som tilldelas här på stacken). bool b = sant; // Tilldelad på stacken. om (b) { // Skapa 500 byte på stacken kolbuffert [500]; // Skapa 500 byte på högen pBuffer = ny röd [500]; } // <- buffert är deallocated här, pBuffer är inte } // <--- oj, det finns en minnesläcka, jag borde ha kallat delete [] pBuffer; | Den viktigaste punkten är att heap and stack är generiska termer för hur minne kan tilldelas. De kan implementeras på många olika sätt och villkoren gäller för de grundläggande begreppen. I en stapel föremål sitter föremålen ovanpå varandra i den ordning de placerades där, och du kan bara ta bort den översta(utan att välta hela saken). Enkelheten med en stack är att du inte behöver underhålla en tabell som innehåller en registrering av varje sektion av tilldelat minne; den enda tillståndsinformation du behöver är en enda pekare till slutet av stacken. För att allokera och de-allokera, ökar och minskar du bara den enskilda pekaren. Obs! En stack kan ibland implementeras för att börja högst upp i en del av minnet och sträcka sig nedåt istället för att växa uppåt. I en hög finns det ingen särskild ordning på hur föremål placeras. Du kan nå och ta bort föremål i valfri ordning eftersom det inte finns någon tydlig "topp" -föremål. Heapallokering kräver att du håller en fullständig registrering av vilket minne som tilldelas och vad som inte är, liksom lite overheadunderhåll för att minska fragmentering, hitta sammanhängande minnessegment som är tillräckligt stora för att passa den önskade storleken och så vidare. Minne kan när som helst omplaceras och lämnar ledigt utrymme. Ibland kommer en minnesallokerare att utföra underhållsuppgifter som att defragmentera minne genom att flytta tilldelat minne eller skräpuppsamling - identifierar vid körning när minnet inte längre är inom räckvidden och omplacerar det. Dessa bilder ska göra ett ganska bra jobb med att beskriva de två sätten att fördela och frigöra minne i en stack och en hög. Namnlösa: Namn! I vilken utsträckning styrs de av operativsystemet eller språktiden? Som nämnts är heap och stack allmänna termer och kan implementeras på många sätt. Datorprogram har vanligtvis en stapel som kallas en samtalsstack som lagrar information som är relevant för den aktuella funktionen, såsom en pekare till vilken funktion den anropades från, och eventuella lokala variabler. Eftersom funktioner ringer till andra funktioner och sedan återvänder växer stacken och krymper för att hålla information från funktionerna längre ner i samtalsstacken. Ett program har inte riktigt runtime-kontroll över det; det bestäms av programmeringsspråket, operativsystemet och till och med systemarkitekturen. En hög är en allmän term som används för alla minne som tilldelas dynamiskt och slumpmässigt. dvs i ordning. Minnet allokeras vanligtvis av operativsystemet, med applikationsanrops-API-funktioner för att göra denna allokering. Det krävs en hel del omkostnader för att hantera dynamiskt allokerat minne, vilket vanligtvis hanteras av körningskoden för det programmeringsspråk eller den använda miljön. Vad är deras omfattning? Samtalstacken är ett så lågt koncept att det inte relaterar till "scope" i betydelsen av programmering. Om du tar isär någon kod ser du relativa pekerstilreferenser till delar av stacken, men när det gäller ett språk på högre nivå inför språket sina egna regler för omfattning. En viktig aspekt av en stack är dock att när en funktion återgår, befrias allt som är lokalt i den funktionen omedelbart från stacken. Det fungerar som du förväntar dig att det fungerar med tanke på hur dina programmeringsspråk fungerar. I en hög är det också svårt att definiera. Omfattningen är vad som exponeras av operativsystemet, men ditt programmeringsspråk lägger förmodligen till sina regler om vad ett "omfång" är i din applikation. Processorarkitekturen och operativsystemet använder virtuell adressering, vilket processorn översätter till fysiska adresser och det finns sidfel etc. De håller reda på vilka sidor som tillhör vilka applikationer. Du behöver egentligen aldrig oroa dig för det här, för du använder bara vilken metod som ditt programmeringsspråk använder för att allokera och frigöra minne, och kontrollera om det finns fel (om tilldelningen / frigöringen misslyckas av någon anledning). Vad avgör storleken på var och en av dem? Återigen beror det på språk, kompilator, operativsystem och arkitektur. En stack tilldelas vanligtvis förut, eftersom den per definition måste vara angränsande minne. Språkkompilatorn eller operativsystemet bestämmer dess storlek. Du lagrar inte stora bitar av data i stacken, så det kommer att vara tillräckligt stort för att det aldrig ska kunna användas helt, förutom i fall av oönskad oändlig rekursion (därmed "stack overflow") eller andra ovanliga programmeringsbeslut. En hög är en allmän term för allt som kan tilldelas dynamiskt. Beroende på vilket sätt du tittar på, förändras den ständigt. I moderna processorer och operativsystem är det exakta sättet att det fungerar väldigt abstrakt, så du behöver normalt inte oroa dig mycket för hur det fungerar djupt ner, förutom att du (på språk där det låter dig) inte får använda du inte har tilldelat ännu eller minne som du har frigjort. Vad gör en snabbare? Stapeln är snabbare eftersom allt ledigt minne alltid är sammanhängande. Ingen lista behöver upprätthållas med alla segment av ledigt minne, bara en enda pekare till den aktuella toppen av stacken. Kompilatorer lagrar vanligtvis denna pekare i ett speciellt, snabbt register för detta ändamål. Dessutom är efterföljande operationer på en stack vanligtvis koncentrerade till mycket närliggande minnesområden, vilket på en mycket låg nivå är bra för optimering av processorn på-diecachar. | (Jag har flyttat det här svaret från en annan fråga som var mer eller mindre en bedragare av den här.) Svaret på din fråga är implementationsspecifikt och kan variera mellan kompilatorer och processorarkitekturer. Här är dock en förenklad förklaring. Både stacken och heapen är minnesområden som tilldelats från det underliggande operativsystemet (ofta virtuellt minne som mappas till fysiskt minne på begäran). I en miljö med flera trådar kommer varje tråd att ha sin egen helt oberoende stack men de delar högen. Samtidig åtkomst måste kontrolleras på högen och är inte möjlig på stacken. Högen Heapen innehåller en länkad lista över begagnade och fria block. Nya tilldelningar på högen (av ny eller malloc) uppfylls genom att skapa ett lämpligt block från ett av de fria blocken. Detta kräver uppdatering av listan över block på högen. Denna metainformation om blocken på högen lagras också på högen ofta i ett litet område precis framför varje block. När högen växer tilldelas nya block ofta från lägre adresser till högre adresser. Således kan du tänka på högen som en hög med minnesblock som växer i storlek när minnet tilldelas. Om högen är för liten för en allokering kan storleken ofta ökas genom att hämta mer minne från det underliggande operativsystemet. Tilldelning och omfördelning av många små block kan lämna högen i ett tillstånd där det finns många små fria block mellan varandra. En begäran om att allokera ett stort block kan misslyckas eftersom inget av de fria blocken är tillräckligt stora för att tillfredsställa allokeringsbegäran trots att de fria blockens kombinerade storlek kan vara tillräckligt stor. Detta kallas högfragmentering. När ett använt block som ligger intill ett fritt block återlokaliseras kan det nya fria blocket slås ihop med det intilliggande fria blocket för att skapa ett större fritt block som effektivt minskar fragmenteringen av högen. Stapeln Stapeln fungerar ofta i nära samverkan med ett specialregister på processorn som heter stackpekaren. Inledningsvis pekar stackpekaren mot toppen av stacken (högsta adressen i stacken). CPU: n har speciella instruktioner för att trycka värden på stacken och hoppa tillbaka dem från stacken. Varje tryck lagrar värdet vid den aktuella platsen för stackpekaren och minskar stackpekaren. En pop hämtar värdet som stackpekaren pekar på och höjer sedan stackpekaren (var inte förvirrad av att lägga till ett värde i stacken minskar stackpekaren och ta bort ett värde ökar den. Kom ihåg att stacken växer till botten). Värdena lagrade och hämtade är värdena i CPU-registren. När en funktion kallas använder CPU specialinstruktioner som skjuter den aktuella instruktionspekaren, dvs adressen till koden som körs på stacken. Processorn hoppar sedan till funktionen genom att ställa in instruktionspekare till adressen till den anropade funktionen. Senare, när funktionen återvänder, poppar den gamla instruktionspekaren från stacken och körningen återupptas vid koden strax efter samtalet till funktionen. När en funktion matas in minskas stackpekaren för att tilldela mer utrymme på stacken för lokala (automatiska) variabler. Om funktionen har en lokal 32-bitars variabel läggs fyra byte åt sidan på stacken. När funktionen återvänder flyttas stackpekaren tillbaka för att frigöra det tilldelade området. Om en funktion har parametrar trycks dessa på stacken innan samtalet till funktionen. Koden i funktionen kan sedan navigera upp i stacken från den aktuella stackpekaren för att lokalisera dessa värden. Häckningsfunktionssamtal fungerar som en charm. Varje nytt samtal tilldelar funktionsparametrar, returadress och utrymme för lokala variabler och dessa aktiveringsposter kan staplas för kapslade samtal och kommer att varva ner på rätt sätt när funktionerna återvänder. Eftersom stacken är ett begränsat minnesblock kan du orsaka ett stacköverflöde genom att anropa för många kapslade funktioner och / eller tilldela för mycket utrymme för lokala variabler. Ofta är minnesområdet som används för stacken inställt på ett sådant sätt att skrivning under botten (den lägsta adressen) på stacken kommer att utlösa en fälla eller undantag i CPU: n. Detta exceptionella tillstånd kan sedan fångas av runtime och konverteras till något slags stacköverflödsundantag. Kan en funktion tilldelas på högen istället för en stack? Nej, aktiveringsposter för funktioner (dvs. lokala eller automatiska variabler) allokeras på stacken som inte bara används för att lagra dessa variabler utan också för att hålla reda på kapslade funktionssamtal. Hur högen hanteras är verkligen upp till runtime-miljön. C använder malloc och C ++ använder nytt, men många andra språk har skräpsamling. Stapeln är dock en mer lågnivåfunktion som är nära knuten till processorarkitekturen. Att odla högen när det inte finns tillräckligt med utrymme är inte så svårt sedan dessdet kan implementeras i bibliotekssamtalet som hanterar högen. Att växa stacken är emellertid ofta omöjligt eftersom stacköverflödet bara upptäcks när det är för sent; och att stänga av körtråden är det enda lönsamma alternativet. | I följande C # -kod public void Method1 () { int i = 4; int y = 2; klass1 cls1 = ny klass1 (); } Så här hanteras minnet Lokala variabler som bara behöver vara så länge som funktionsanropet går i stacken. Heapen används för variabler vars livstid vi inte riktigt känner till framåt men vi förväntar oss att de ska ta ett tag. På de flesta språk är det viktigt att vi vid sammanställningstid vet hur stor en variabel är om vi vill lagra den på stacken. Objekt (som varierar i storlek när vi uppdaterar dem) går på högen eftersom vi inte vet vid skapelsestiden hur länge de ska hålla. På många språk samlas heapen för att hitta objekt (som cls1-objektet) som inte längre har några referenser. I Java går de flesta föremål direkt i högen. På språk som C / C ++ kan strukturer och klasser ofta finnas kvar på stacken när du inte har att göra med pekare. Mer information finns här: Skillnaden mellan tilldelning av stack och heapminne «timmurphy.org och här: Skapa objekt på stapeln och högen Den här artikeln är källan till bilden ovan: Sex viktiga .NET-koncept: Stack, heap, värdetyper, referenstyper, boxning och unboxing - CodeProject men tänk på att det kan innehålla vissa felaktigheter. | Stack När du anropar en funktion läggs argumenten till den funktionen plus några andra omkostnader på stacken. Viss info (t.ex. vart man ska åka tillbaka) lagras också där. När du förklarar en variabel inuti din funktion allokeras den variabeln också på stacken. Att omlokalisera stapeln är ganska enkelt eftersom du alltid omplacerar i omvänd ordning som du tilldelar. Stack grejer läggs till när du går in i funktioner, motsvarande data tas bort när du avslutar dem. Det betyder att du tenderar att stanna inom en liten del av stacken såvida du inte ringer till många funktioner som anropar många andra funktioner (eller skapar en rekursiv lösning). Heapen Heapen är ett generiskt namn för var du placerar data som du skapar i farten. Om du inte vet hur många rymdskepp som ditt program kommer att skapa kommer du troligen att använda den nya (eller malloc eller motsvarande) operatören för att skapa varje rymdskepp. Denna fördelning kommer att hålla kvar ett tag, så det är troligt att vi kommer att frigöra saker i en annan ordning än vi skapade dem. Således är högen mycket mer komplex, eftersom det slutligen blir minnesregioner som är oanvända sammanflätade med bitar som är - minnet blir fragmenterat. Att hitta ledigt minne i den storlek du behöver är ett svårt problem. Det är därför som högen bör undvikas (även om den fortfarande används ofta). Genomförande Implementeringen av både stack och heap beror vanligtvis på runtime / OS. Ofta skapar spel och andra applikationer som är prestandakritiska sina egna minneslösningar som tar en stor bit minne från högen och sedan rensar ut det internt för att undvika att förlita sig på operativsystemet för minne. Detta är bara praktiskt om din minnesanvändning är helt annorlunda än normen - det vill säga för spel där du laddar en nivå i en enorm operation och kan chucka bort hela partiet i en annan enorm operation. Fysisk plats i minnet Detta är mindre relevant än du tror på grund av en teknik som heter Virtual Memory som får ditt program att tro att du har tillgång till en viss adress där den fysiska informationen finns någon annanstans (även på hårddisken!). Adresserna du får för stacken är i ökande ordning när ditt samtalsträd blir djupare. Adresserna till högen är oförutsägbara (dvs. implimenteringsspecifika) och uppriktigt sagt inte viktiga. | För att klargöra har detta svar felaktig information (Thomas fixade sitt svar efter kommentarer, coolt :)). Andra svar undviker bara att förklara vad statisk allokering betyder. Så jag kommer att förklara de tre huvudformerna för allokering och hur de vanligtvis relaterar till heap-, stack- och datasegmentet nedan. Jag kommer också att visa några exempel i både C / C ++ och Python för att hjälpa människor att förstå. "Statiska" (AKA-statiskt tilldelade) variabler tilldelas inte på stacken. Antag inte det - många människor gör bara för att "statisk" låter mycket som "stack". De finns faktiskt varken i stacken eller högen. De är en del av det som kallas datasegmentet. Det är dock i allmänhet bättre att överväga "scope" och "lifetime" snarare än "stack" och "heap". Omfattning avser vilka delar av koden som kan komma åt en variabel. Generellt tänker vi på lokal räckvidd (kan endast nås med den nuvarande funktionen) kontra globalt räckvidd (kan nås var som helst) även om räckvidden kan bli mycket mer komplex. Livstid avser när en variabel tilldelas och omplaceras under programkörning. Vanligtvis tänker vi på statisk allokering (variabelkommer att bestå under hela programmets varaktighet, vilket gör det användbart för att lagra samma information över flera funktionsanrop) kontra automatisk allokering (variabel kvarstår bara under ett enda samtal till en funktion, vilket gör den användbar för att lagra information som bara används under din funktion och kan kasseras när du är klar) kontra dynamisk fördelning (variabler vars varaktighet definieras vid körning, istället för kompileringstid som statisk eller automatisk). Även om de flesta kompilatorer och tolkar implementerar detta beteende på samma sätt när det gäller att använda stackar, högar etc. kan en kompilator ibland bryta dessa konventioner om den vill så länge som beteendet är korrekt. Till exempel, på grund av optimering kan en lokal variabel bara finnas i ett register eller tas bort helt, även om de flesta lokala variabler finns i stacken. Som har påpekats i några kommentarer är du fri att implementera en kompilator som inte ens använder en stack eller en hög, utan istället några andra lagringsmekanismer (sällan gjort, eftersom staplar och högar är bra för detta). Jag kommer att ge några enkla kommenterade C-koder för att illustrera allt detta. Det bästa sättet att lära sig är att köra ett program under en felsökare och titta på beteendet. Om du föredrar att läsa python, hoppa till slutet av svaret :) // Statiskt allokerat i datasegmentet när programmet / DLL laddas först // Deallocated när programmet / DLL avslutas // scope - kan nås var som helst i koden int someGlobalVariable; // Tilldelas statistiskt i datasegmentet när programmet laddas först // Omplaceras när programmet / DLL avslutas // scope - kan nås var som helst i just den här kodfilen statisk int someStaticVariable; // "someArgument" tilldelas på stacken varje gång MyFunction anropas // "someArgument" deallocated när MyFunction återvänder // scope - kan endast nås inom MyFunction () ogiltig MyFunction (int someArgument) { // Tilldelas statistiskt i datasegmentet när programmet laddas först // Deallocated när programmet / DLL avslutas // scope - kan endast nås inom MyFunction () statisk int someLocalStaticVariable; // Tilldelas på stacken varje gång MyFunction anropas // Deallocated när MyFunction återvänder // scope - kan endast nås inom MyFunction () int someLocalVariable; // En * pekare * tilldelas på stacken varje gång MyFunction anropas // Den här pekaren delas om när MyFunction återvänder // scope - pekaren kan endast nås inom MyFunction () int * someDynamicVariable; // Denna rad orsakar utrymme för ett heltal som tilldelas i högen // när denna rad körs. Observera att detta inte är i början av // samtalet till MyFunction (), som de automatiska variablerna // scope - endast kod inom MyFunction () kan komma åt detta utrymme // * genom denna speciella variabel *. // Men om du skickar adressen någon annanstans, den koden // kan komma åt den också someDynamicVariable = new int; // Denna rad deallocates utrymmet för heltalet i högen. // Om vi inte skrev det skulle minnet "läcka ut". // Observera en grundläggande skillnad mellan stacken och högen // högen måste hanteras. Stapeln hanteras för oss. ta bort someDynamicVariable; // I andra fall, istället för att omplacera detta högutrymme du // kan lagra adressen någonstans mer permanent att använda senare. // Vissa språk tar till och med hand om placeringen åt dig ... men // alltid måste det tas om hand vid körning av någon mekanism. // När funktionen återvänder, someArgument, someLocalVariable // och pekaren someDynamicVariable är omplacerade. // Utrymmet som visades av someDynamicVariable var redan // deallocated före retur. lämna tillbaka; } // Observera att someGlobalVariable, someStaticVariable och // someLocalStaticVariable fortsätter att existera och är inte // deallocated tills programmet avslutas. Ett särskilt gripande exempel på varför det är viktigt att skilja mellan livslängd och omfång är att en variabel kan ha lokalt omfång men statisk livslängd - till exempel "someLocalStaticVariable" i kodprovet ovan. Sådana variabler kan göra våra vanliga men informella namnvanor mycket förvirrande. Till exempel när vi säger "lokal" menar vi vanligtvis "automatiskt allokerad variabel" och när vi säger global menar vi vanligtvis "statligt allokerad variabel globalt". Tyvärr när det gäller saker som "filomfattade statiskt tilldelade variabler" säger många bara ... "va ???". Några av syntaxvalen i C / C ++ förvärrar detta problem - till exempel tycker många att globala variabler inte är "statiska" på grund av syntaxen som visas nedan. int var1; // Har global omfattning och statisk tilldelning statisk int var2; // Har filomfång och statisk tilldelning int main () {return 0;} Observera att placeringen av nyckelordet "statisk" i deklarationen ovan förhindrar att var2 har globalt omfång. Ändå har den globala var1 statisk allokering. Det här är inteintuitiv! Av denna anledning försöker jag aldrig använda ordet "statisk" när jag beskriver omfattning, och istället säga något som "fil" eller "filbegränsat" omfång. Men många använder frasen "statisk" eller "statisk räckvidd" för att beskriva en variabel som endast kan nås från en kodfil. I livstidssammanhang betyder "statisk" alltid att variabeln allokeras vid programstart och omplaceras när programmet avslutas. Vissa människor tänker på dessa begrepp som C / C ++ specifika. De är inte. Till exempel illustrerar Python-exemplet nedan alla tre typer av tilldelning (det finns några subtila skillnader i tolkade språk som jag inte kommer in på här). från datetime importera datetime klass Djur: _FavoriteFood = 'Odefinierad' # _FavoriteFood tilldelas statiskt def PetAnimal (själv): curTime = datetime.time (datetime.now ()) # curTime tilldelas automatiskt skriv ut ("Tack för att du klappade mig. Men det är" + str (curTime) + ", du borde mata mig. Min favoritmat är" + self._FavoriteFood) klass Katt (djur): _FavoriteFood = 'tonfisk' # Obs! Eftersom vi åsidosätter har Cat-klassen sin egen statiskt tilldelade _FavoriteFood-variabel, annorlunda än djurets klass Hund (djur): _FavoriteFood = 'biff' # På samma sätt får hundklassen sin egen statiska variabel. Viktigt att notera - den här statiska variabeln delas mellan alla instanser av hund, därför är den inte dynamisk! om __name__ == "__main__": morrhår = Cat () # Dynamiskt tilldelad fido = Hund () # Dynamiskt allokerad rinTinTin = Hund () # Tilldelad dynamiskt morrhår. PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () Dog._FavoriteFood = 'mjölkben' morrhår. PetAnimal () fido.PetAnimal () rinTinTin.PetAnimal () # Output är: # Tack för att du klappade mig. Men det är 13: 05: 02.255000, du borde mata mig. Min favoritmat är tonfisk # Tack för att du klappade mig. Men det är 13: 05: 02.255000, du borde mata mig. Min favoritmat är biff # Tack för att du klappade mig. Men det är 13: 05: 02.255000, du borde mata mig. Min favoritmat är biff # Tack för att du klappade mig. Men det är 13: 05: 02.255000, du borde mata mig. Min favoritmat är tonfisk # Tack för att du klappade mig. Men det är 13: 05: 02.255000, du borde mata mig. Min favoritmat är mjölkben # Tack för att du klappade mig. Men det är 13: 05: 02.256000, du borde mata mig. Min favoritmat är mjölkben | Andra har svarat på de stora slagen ganska bra, så jag slänger in några detaljer. Stack och heap behöver inte vara singular. En vanlig situation där du har mer än en stack är om du har mer än en tråd i en process. I det här fallet har varje tråd sin egen stack. Du kan också ha mer än en hög, till exempel kan vissa DLL-konfigurationer resultera i att olika DLL-filer allokeras från olika högar, varför det i allmänhet är en dålig idé att släppa minne som tilldelats av ett annat bibliotek. I C kan du dra nytta av allokering med variabel längd genom användning av alloca, som allokeras på stacken, i motsats till alloc, som tilldelas på högen. Det här minnet överlever inte ditt returuttalande, men det är användbart för en skrapbuffert. Att göra en enorm tillfällig buffert på Windows som du inte använder mycket av är inte gratis. Detta beror på att kompilatorn kommer att generera en stack-sondslinga som anropas varje gång din funktion går in för att se till att stacken finns (eftersom Windows använder en enda skyddsida i slutet av din stack för att upptäcka när den behöver växa stacken. Om du får åtkomst till minne mer än en sida från slutet av stacken kommer du att krascha). Exempel: ogiltig min funktion () { röding stor [10000000]; // Gör något som bara används för första 1K av stora 99% av tiden. } | Andra har svarat direkt på din fråga, men när jag försöker förstå stacken och högen tror jag att det är bra att överväga minneslayouten för en traditionell UNIX-process (utan trådar och mmap () -baserade allokatorer). Webbplatsen Memory Management Glossary har ett diagram över denna minneslayout. Stapeln och högen är traditionellt placerade i motsatta ändar av processens virtuella adressutrymme. Stapeln växer automatiskt när den nås, upp till en storlek som ställs in av kärnan (som kan justeras med setrlimit (RLIMIT_STACK, ...)). Heapen växer när minnesallokatorn anropar systemanropet brk () eller sbrk () och mappar fler sidor med fysiskt minne i processens virtuella adressutrymme. I system utan virtuellt minne, till exempel vissa inbäddade system, gäller ofta samma grundläggande layout, förutom att stacken och högen är fasta i storlek. I andra inbäddade system (som de som är baserade på Microchip PIC-mikrokontroller) är dock programstacken ett separat minnesblock som inte kan adresseras av instruktioner för dataförflyttning, och kan endast modifieras eller läsas indirekt genom programflödesinstruktioner (ring retur osv.). Andra arkitekturer, som Intel Itanium-processorer, har flera stackar. I denna mening är stacken ett element i CPU-arkitekturen. | Stapeln är en delminne som kan manipuleras via flera nyckelmonteringsspråkinstruktioner, till exempel "pop" (ta bort och returnera ett värde från stacken) och "push" (tryck ett värde till stacken), men ring också (ring en underrutin - detta trycker på adressen för att återvända till stacken) och returnera (returnera från en underrutin - detta dyker upp adressen från stacken och hoppar till den). Det är minnesområdet under stackpekarregistret, som kan ställas in efter behov. Stapeln används också för att skicka argument till underrutiner, och även för att bevara värdena i register innan du anropar underrutiner. Heapen är en del av minnet som ges till en applikation av operativsystemet, vanligtvis genom en syscall som malloc. På moderna operativsystem är detta minne en uppsättning sidor som endast anropsprocessen har tillgång till. Storleken på stacken bestäms vid körning och växer vanligtvis inte efter att programmet startat. I ett C-program måste stacken vara tillräckligt stor för att hålla varje variabel som deklareras inom varje funktion. Heapen kommer att växa dynamiskt efter behov, men OS gör i slutändan samtalet (det kommer ofta att växa heapen med mer än det värde som malloc begär, så att åtminstone vissa framtida mallocs inte behöver gå tillbaka till kärnan till få mer minne. Detta beteende kan ofta anpassas) Eftersom du har tilldelat stacken innan du startar programmet behöver du aldrig malloc innan du kan använda stacken, så det är en liten fördel där. I praktiken är det väldigt svårt att förutsäga vad som kommer att vara snabbt och vad som kommer att vara långsamt i moderna operativsystem som har undersystem för virtuellt minne, för hur sidorna implementeras och var de lagras är en implementeringsdetalj. | Vad är en stack? En stack är en hög med objekt, vanligtvis en som är snyggt ordnad. Travar i dataarkitekturer är minnesregioner där data läggs till eller tas bort på ett sista-in-först-ut-sätt. I en applikation med flera trådar kommer varje tråd att ha sin egen stack. Vad är en hög? En hög är en stökig samling saker staplade upp slumpmässigt. I dataarkitekturer är högen ett område med dynamiskt tilldelat minne som hanteras automatiskt av operativsystemet eller minneshanterarbiblioteket. Minne på högen tilldelas, omplaceras och storleksändras regelbundet under programkörning, och detta kan leda till ett problem som kallas fragmentering. Fragmentering uppstår när minnesobjekt allokeras med små mellanrum som är för små för att rymma ytterligare minnesobjekt. Nettoresultatet är en procentandel av heaputrymmet som inte kan användas för ytterligare minnesallokeringar. Båda tillsammans I en applikation med flera trådar kommer varje tråd att ha sin egen stack. Men alla olika trådar kommer att dela högen. Eftersom de olika trådarna delar högen i en applikation med flera trådar betyder det också att det måste finnas en viss samordning mellan trådarna så att de inte försöker komma åt och manipulera samma minnesbit i högen på samtidigt. Vilket är snabbare - stacken eller högen? Och varför? Stapeln är mycket snabbare än högen. Detta beror på hur minnet allokeras på stacken. Att fördela minne på stacken är lika enkelt som att flytta stackpekaren uppåt. För personer som är nya inom programmering är det förmodligen en bra idé att använda stacken eftersom det är lättare. Eftersom stacken är liten vill du använda den när du vet exakt hur mycket minne du behöver för dina data, eller om du vet att storleken på dina data är mycket liten. Det är bättre att använda högen när du vet att du kommer att behöva mycket minne för dina data, eller om du bara inte är säker på hur mycket minne du behöver (som med en dynamisk matris). Java-minnesmodell Stapeln är minnesområdet där lokala variabler (inklusive metodparametrar) lagras. När det gäller objektvariabler är det bara referenser (pekare) till de faktiska objekten på högen. Varje gång ett objekt instansieras, läggs en bit högminne åt sidan för att hålla data (tillstånd) för det objektet. Eftersom objekt kan innehålla andra objekt kan en del av denna data faktiskt innehålla referenser till de kapslade objekten. | Jag tror att många andra har gett dig mest korrekta svar i denna fråga. En detalj som har missats är dock att "högen" i själva verket sannolikt borde kallas "fri butik". Anledningen till denna skillnad är att den ursprungliga fria butiken implementerades med en datastruktur som kallas en "binomial heap". Av den anledningen var allokering från en tidig implementering av malloc () / free () tilldelning från en hög. Men i denna moderna tid implementeras de flesta gratisbutiker med mycket detaljerade datastrukturer som inte är binomiala högar. | Du kan göra några intressanta saker med stacken. Till exempel har du funktioner som alloca (förutsatt att du kan komma förbi de rikliga varningarna om dess användning), vilket är en form av malloc somanvänder specifikt stacken, inte högen, för minne. Med det sagt är stackbaserade minnesfel några av de värsta jag har upplevt. Om du använder högminne och överskrider gränserna för ditt tilldelade block har du en anständig chans att utlösa ett segmentfel. (Inte 100%: ditt block kan för övrigt vara angränsande till ett annat som du tidigare har tilldelat.) Men eftersom variabler som skapas i stacken alltid är angränsande till varandra kan skrivning av gränser ändra värdet på en annan variabel. Jag har lärt mig att när jag känner att mitt program har slutat följa logikens lagar är det förmodligen buffertöverskridande. | Enkelt, stacken är där lokala variabler skapas. Varje gång du ringer till en underrutin kommer också programräknaren (pekaren till nästa maskininstruktion) och några viktiga register, och ibland skjuts parametrarna på stacken. Sedan skjuts alla lokala variabler inuti subrutinen på stacken (och används därifrån). När subrutinen är klar, dyker alla saker tillbaka från stacken. Datorn för PC och register kommer och läggs tillbaka där den var när den poppades, så att ditt program kan gå på sin glada väg. Heapen är minnesområdet dynamiska minnesallokeringar görs av (uttryckliga "nya" eller "allokera" samtal). Det är en speciell datastruktur som kan hålla koll på minnesblock i olika storlekar och deras allokeringsstatus. I "klassiska" system lagrades RAM ut så att stackpekaren började längst ner i minnet, högpekaren började högst upp och de växte mot varandra. Om de överlappar var du tom för RAM. Det fungerar dock inte med moderna operativsystem med flera trådar. Varje tråd måste ha sin egen stack, och de kan skapas dynamiskt. | Från WikiAnwser. Stack När en funktion eller en metod anropar en annan funktion som i sin tur anropar en annan funktion, etc., förblir exekveringen av alla dessa funktioner tills den sista funktionen returnerar sitt värde. Denna kedja av avstängda funktionssamtal är stacken, eftersom element i stacken (funktionsanrop) är beroende av varandra. Stapeln är viktig att beakta vid hantering av undantag och körningar av trådar. Högen Heapen är helt enkelt det minne som används av program för att lagra variabler. Element i högen (variabler) har inga beroenden med varandra och kan alltid nås slumpmässigt när som helst. | Stack Mycket snabb åtkomst Behöver inte uttryckligen avdela variabler Utrymmet hanteras effektivt av CPU, minnet blir inte fragmenterat Endast lokala variabler Begränsa stackstorleken (OS-beroende) Variabler kan inte ändras Högen Variabler kan nås globalt Ingen gräns för minnesstorlek (Relativt) långsammare åtkomst Ingen garanterad effektiv användning av utrymme, minnet kan bli fragmenterat med tiden när minnesblock tilldelas och sedan frigöras Du måste hantera minne (du har ansvaret för att tilldela och frigöra variabler) Variabler kan ändras med realloc () | Kortfattat En stack används för statisk minnestilldelning och en hög för dynamisk minnestilldelning, båda lagrade i datorns RAM. I detalj Stack Stapeln är en "LIFO" (sista in, först ut) datastruktur, som hanteras och optimeras av CPU ganska nära. Varje gång en funktion deklarerar en ny variabel, trycks den på stacken. Sedan frigörs alla variabler som trycks på stacken av den funktionen varje gång en funktion avslutas (det vill säga de raderas). När en stackvariabel har frigörts blir minnesregionen tillgänglig för andra stackvariabler. Fördelen med att använda stacken för att lagra variabler är att minnet hanteras för dig. Du behöver inte tilldela minne för hand eller frigöra det när du inte behöver det längre. Vad mer, eftersom CPU: n organiserar stackminnet så effektivt är det mycket snabbt att läsa från och skriva till stackvariabler. Mer finns här. Heapen Heapen är en region i datorns minne som inte hanteras automatiskt för dig och som inte hanteras lika hårt av CPU: n. Det är en mer fritt flytande minnesregion (och är större). För att fördela minne på högen måste du använda malloc () eller calloc (), som är inbyggda C-funktioner. När du har tilldelat minne på högen är du ansvarig för att använda free () för att omplacera minnet när du inte behöver det längre. Om du misslyckas med att göra detta kommer ditt program att ha så kallat minnesläcka. Det vill säga minnet på högen kommer fortfarande att läggas åt sidan (och kommer inte att vara tillgängligt för andra processer). Som vi kommer att se i felsökningsavsnittet finns det ett verktyg som heter Valgrind som kan hjälpa dig att upptäcka minnesläckor. Till skillnad från stacken har högen inte storleksbegränsningar för variabel storlek (bortsett från de uppenbara fysiska begränsningarna på din dator). Heapminne är något långsammare att läsa från och skriva till, eftersom man måste använda pekare för att komma åt minne på heapen. Vi kommer att prata om pekare inom kort. Till skillnad från stacken,variabler som skapas på högen är tillgängliga för alla funktioner, var som helst i ditt program. Heapvariabler är i huvudsak globala. Mer finns här. Variabler som tilldelats på stacken lagras direkt i minnet och tillgången till detta minne är mycket snabb och dess allokering hanteras när programmet kompileras. När en funktion eller en metod anropar en annan funktion som i sin tur anropar en annan funktion, etc., förblir exekveringen av alla dessa funktioner tills den sista funktionen returnerar sitt värde. Stapeln är alltid reserverad i en LIFO-ordning, det senast reserverade blocket är alltid nästa block som ska frigöras. Detta gör det väldigt enkelt att hålla koll på stacken, att frigöra ett block från stacken är inget annat än att justera en pekare. Variabler som tilldelats på högen har sitt minne tilldelat vid körningstid och åtkomst till detta minne är lite långsammare, men högstorleken begränsas bara av storleken på det virtuella minnet. Elementen på högen har inga beroende av varandra och kan alltid nås slumpmässigt när som helst. Du kan tilldela ett block när som helst och frigöra det när som helst. Detta gör det mycket mer komplicerat att hålla reda på vilka delar av högen som tilldelas eller är gratis vid en given tidpunkt. Du kan använda stacken om du vet exakt hur mycket data du behöver allokera innan kompileringstiden, och den är inte för stor. Du kan använda högen om du inte vet exakt hur mycket data du behöver vid körning eller om du behöver allokera mycket data. I en situation med flera trådar kommer varje tråd att ha sin egen helt oberoende stack, men de delar högen. Stapeln är trådspecifik och högen är applikationsspecifik. Stapeln är viktig att beakta vid hantering av undantag och körningar av trådar. Varje tråd får en stack, medan det vanligtvis bara finns en hög för applikationen (även om det inte är ovanligt att ha flera högar för olika typer av tilldelning). Vid körning, om applikationen behöver mer hög, kan den allokera minne från ledigt minne och om stacken behöver minne kan den allokera minne från ledigt minne tilldelat minne för applikationen. Till och med, mer information ges här och här. Kom nu till svaret på din fråga. I vilken utsträckning styrs de av operativsystemet eller språktiden? OS tilldelar stacken för varje tråd på systemnivå när tråden skapas. Vanligtvis kallas operativsystemet av språktiden för att allokera högen för applikationen. Mer finns här. Vad är deras omfattning? Redan i toppen. "Du kan använda stacken om du vet exakt hur mycket data du behöver allokera innan kompileringstiden och den inte är för stor. Du kan använda högen om du inte vet exakt hur mycket data du behöver vid körning eller om du måste allokera mycket data. " Mer finns här. Vad avgör storleken på var och en av dem? Storleken på stacken ställs in av OS när en tråd skapas. Storleken på högen ställs in vid start av programmet, men den kan växa när det behövs utrymme (fördelaren begär mer minne från operativsystemet). Vad gör en snabbare? Stackallokering är mycket snabbare eftersom allt det verkligen gör är att flytta stackpekaren. Med hjälp av minnespooler kan du få jämförbar prestanda ur högtilldelning, men det kommer med en liten extra komplexitet och sin egen huvudvärk. Stack, heap är inte bara en prestationsbedömning; det berättar också mycket om objektens förväntade livstid. Detaljer hittar du härifrån. | OK, helt enkelt och med korta ord menar de beställt och inte beställt ...! Stack: I stackartiklar kommer saker på varandra, vilket innebär att det blir snabbare och effektivare att bearbeta! ... Så det finns alltid ett index för att peka på det specifika objektet, bearbetningen kommer också att bli snabbare, det finns också ett samband mellan artiklarna! ... Heap: Ingen ordning, bearbetningen kommer att bli långsammare och värdena blir trasslade tillsammans utan någon specifik ordning eller index ... det är slumpmässigt och det finns inget samband mellan dem ... så körning och användningstid kan variera ... Jag skapar också bilden nedan för att visa hur de kan se ut: | stack, heap och data för varje process i virtuellt minne: | På 1980-talet förökade UNIX sig som kaniner med stora företag som rullade sina egna. Exxon hade en som dussintals varumärken förlorade för historien. Hur minnet lagts ut bestämdes av många implementatorer. Ett typiskt C-program lades ut i minnet med en möjlighet att öka genom att ändra brk () -värdet. Vanligtvis var HEAP strax under detta brk-värde och ökande brk ökade mängden tillgänglig hög. Singeln STACK var typiskt ett område under HEAP som var ett minne som inte innehåller något av värde tills toppen av nästa fixerade minnesblock. Detta nästa block var ofta CODE som kunde skrivas över av stackdata i en av de berömda hackarna från sin tid. Ett typiskt minnesblock var BSS (ett nollblockvärden) som av misstag inte nollställts i en tillverkares erbjudande. En annan var DATA som innehöll initialiserade värden, inklusive strängar och siffror. En tredje var CODE som innehöll CRT (C runtime), main, funktioner och bibliotek. Tillkomsten av virtuellt minne i UNIX ändrar många av begränsningarna. Det finns ingen objektiv anledning till varför dessa block behöver vara angränsande, eller fast i storlek, eller beställt ett visst sätt nu. Naturligtvis före UNIX var Multics som inte led av dessa begränsningar. Här är en schematisk bild som visar en av minneslayouterna i den eran. | Ett par cent: Jag tror att det kommer att vara bra att rita minnet grafiskt och enklare: Pilar - visa var grow stack och heap, process stack storlek har gräns, definierad i OS, tråd stack storlek begränsningar av parametrar i thread create API vanligtvis. Heap brukar begränsas av processens maximala virtuella minnesstorlek, till exempel 32 bitar 2-4 GB. Så enkelt sätt: processheap är allmänt för process och alla trådar inuti, och används för allokering av minne i vanliga fall med något som malloc (). Stack är snabbminne för lagring av vanliga pekare och variabler för funktionsreturer, bearbetade som parametrar i funktionsanrop, lokala funktionsvariabler. | Eftersom några svar gick på att tappa, kommer jag att bidra med min kvalster. Överraskande har ingen nämnt att flera (dvs inte relaterade till antalet körande OS-nivå-trådar) samtalsstackar inte bara finns på exotiska språk (PostScript) eller plattformar (Intel Itanium) utan också i fibrer, gröna trådar och några implementeringar av coroutines. Fibrer, gröna trådar och koroutiner är på många sätt lika, vilket leder till mycket förvirring. Skillnaden mellan fibrer och gröna trådar är att den förstnämnda använder samarbetsmultitasking, medan den senare kan innehålla antingen samarbetsvillig eller förebyggande (eller till och med båda). För skillnaden mellan fibrer och koroutiner, se här. I vilket fall som helst är syftet med både fibrer, gröna trådar och koroutiner att ha flera funktioner som utförs samtidigt, men inte parallellt (se denna SO-fråga för åtskillnad) inom en enda OS-nivå tråd, överföra kontroll fram och tillbaka från varandra på ett organiserat sätt. När du använder fibrer, gröna trådar eller coroutines har du vanligtvis en separat stack per funktion. (Tekniskt sett är inte bara en stack utan ett helt körningskontext per funktion. Viktigast av allt registreras CPU.) För varje tråd finns det lika många stackar som det finns funktioner som körs samtidigt, och tråden växlar mellan att utföra varje funktion enligt logik i ditt program. När en funktion går till slutet förstörs dess stack. Så, antalet och livslängderna för stackar är dynamiska och bestäms inte av antalet trådar på OS-nivå! Observera att jag sa "har vanligtvis en separat stack per funktion". Det finns både stackiga och stacklösa implementeringar av couroutines. De mest anmärkningsvärda stackfulla C ++ - implementeringarna är Boost.Coroutine och Microsoft PPL: s async / await. (C ++: s återupptagningsfunktioner (även kallad "async and await"), som föreslogs till C ++ 17, kommer sannolikt att använda stapelfria coroutines.) Fiberförslag till C ++ - standardbiblioteket kommer. Det finns också några tredjepartsbibliotek. Gröna trådar är extremt populära på språk som Python och Ruby. | Jag har något att dela, även om de viktigaste punkterna redan är täckta. Stack Mycket snabb åtkomst. Lagrad i RAM. Här laddas funktionssamtal tillsammans med de lokala variablerna och skickade funktionsparametrarna. Utrymme frigörs automatiskt när programmet går utanför ett omfång. Lagras i sekventiellt minne. Högen Långsam åtkomst jämfört med Stack. Lagrad i RAM. Här lagras dynamiskt skapade variabler, vilket senare kräver att det tilldelade minnet frigörs efter användning. Lagras var minnesallokering görs, alltid nås av pekaren. Intressant anmärkning: Om funktionsanropen hade lagrats i hög, skulle det ha resulterat i 2 stökiga poäng: På grund av sekventiell lagring i stack är körningen snabbare. Lagring i hög skulle ha resulterat i enorm tidsförbrukning och därmed gjort hela programmet långsammare. Om funktioner lagrades i hög (rörigt lagringsutrymme pekad av pekaren), skulle det inte ha varit något sätt att återvända till den uppringande adressen tillbaka (vilken stack ger på grund av sekventiell lagring i minnet). | Wow! Så många svar och jag tror inte att en av dem fick det rätt ... 1) Var och vad är de (fysiskt i en riktig dators minne)? Stapeln är minne som börjar som den högsta minnesadressen som tilldelats din programbild och sedan minskar värdet därifrån. Den är reserverad för kallade funktionsparametrar och för alla temporära variabler som används i funktioner. Det finns två högar: offentliga och privata. Den privata högen börjar på en 16-byte-gräns (för 64-bitarsprogram) eller en 8-byte-gräns (för 32-bitarsprogram) efter den sista byten med kod i ditt program och ökar sedan ivärde därifrån. Det kallas också standardhög. Om den privata högen blir för stor överlappar den stapelområdet, liksom stapeln överlappar högen om den blir för stor. Eftersom stacken börjar vid en högre adress och går ner till lägre adress, med rätt hacking kan du göra stacken så stor att den kommer att överskrida det privata högområdet och överlappa kodområdet. Tricket är då att överlappa tillräckligt med kodområdet så att du kan ansluta till koden. Det är lite knepigt att göra och du riskerar en programkrasch, men det är enkelt och mycket effektivt. Den offentliga högen finns i sitt eget minnesutrymme utanför ditt programbildutrymme. Det är detta minne som kommer att sippas ut på hårddisken om minnesresurser blir knappa. 2) I vilken utsträckning styrs de av operativsystemet eller språktiden? Stapeln styrs av programmeraren, den privata högen hanteras av operativsystemet och den offentliga högen styrs inte av någon eftersom det är en OS-tjänst - du gör förfrågningar och antingen beviljas eller nekas. 2b) Vad är deras omfattning? De är alla globala för programmet, men deras innehåll kan vara privat, offentligt eller globalt. 2c) Vad avgör storleken på var och en av dem? Storleken på stacken och den privata högen bestäms av alternativen för kompilatorn. Den offentliga högen initialiseras vid körning med hjälp av en storleksparameter. 2d) Vad gör en snabbare? De är inte utformade för att vara snabba, de är utformade för att vara användbara. Hur programmeraren använder dem avgör om de är "snabba" eller "långsamma" REF: https://norasandler.com/2019/02/18/Write-a-Compiler-10.html https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-getprocessheap https://docs.microsoft.com/en-us/windows/desktop/api/heapapi/nf-heapapi-heapcreate | Många svar är korrekta som begrepp, men vi måste notera att en stack behövs av hårdvaran (dvs. mikroprocessor) för att tillåta anrop av underrutiner (CALL på monteringsspråk ..). (OOP-killar kommer att kalla det metoder) På stacken sparar du returadresser och samtal → push / ret → pop hanteras direkt i hårdvara. Du kan använda stacken för att skicka parametrar ... även om den är långsammare än att använda register (skulle en mikroprocessorguru säga eller en bra BIOS-bok från 1980-talet ...) Utan stack kan ingen mikroprocessor fungera. (vi kan inte föreställa oss ett program, inte ens i monteringsspråk, utan underrutiner / funktioner) Utan högen kan det. (Ett monteringsspråkprogram kan fungera utan, eftersom heapen är ett OS-koncept, som malloc, det vill säga ett OS / Lib-samtal. Stackanvändningen är snabbare eftersom: Är hårdvara, och även push / pop är mycket effektiva. malloc kräver att man går in i kärnläge, använder lås / semafor (eller andra synkroniseringsprimitiv) som kör någon kod och hanterar några strukturer som behövs för att hålla reda på allokeringen. | Heap är ett område med dynamiskt tilldelat minne som hanteras automatiskt av operativsystemet eller minneshanterarbiblioteket. Du kan tilldela ett block när som helst och frigöra det när som helst. Heapallokering kräver att du håller en fullständig registrering av vilket minne som tilldelas och vad som inte är, liksom lite overheadunderhåll för att minska fragmentering, hitta angränsande minnessegment som är tillräckligt stora för att passa den önskade storleken och så vidare. Minne kan när som helst omplaceras och lämnar ledigt utrymme. När högen växer tilldelas nya block ofta från lägre adresser till högre adresser. Således kan du tänka på högen som en hög med minnesblock som växer i storlek när minnet tilldelas. Om högen är för liten för en allokering kan storleken ofta ökas genom att hämta mer minne från det underliggande operativsystemet. Minne som tilldelats från högen förblir allokerat tills något av följande inträffar: Minnet frigörs Programmet avslutas Stack: Lagras i datorns RAM precis som högen. Variabler som skapas i stacken kommer att räcka ut och omlokaliseras automatiskt. Mycket snabbare att fördela jämfört med variabler på högen. Lagrar lokal data, returadresser, används för parameteröverföring. Kan ha ett stacköverflöde när för mycket av stacken används (mestadels från oändlig eller för djup rekursion, mycket stora tilldelningar). Du skulle använda stacken om du vet exakt hur mycket data du behöver fördela före kompileringstid och den är inte för stor. Har vanligtvis en maximal storlek redan bestämd när ditt program startar. Högen: Lagras i datorns RAM precis som stacken. I C ++ måste variabler på högen förstöras manuellt och aldrig faller utanför räckvidden. Uppgifterna frigörs med radering, radering [] eller gratis. Långsammare att fördela jämfört med variabler i stacken. Används vid behov för att allokera ett datablock för användning av programmet. Kan ha fragmentering när det finns många tilldelningar och deallokationer. I C ++ eller C kommer data som skapas på högen att pekas på av pekare och tilldelas med nya respektive malloc. Kan ha allokeringsfel om för stor buffert begärs tilldelas. Duskulle använda högen om du inte vet exakt hur mycket data du behöver vid körning eller om du behöver allokera mycket data. Ansvarig för minnesläckor. | Stapeln är i huvudsak ett lättillgängligt minne som helt enkelt hanterar sina objekt som en - väl - stack. Endast föremål för vilka storleken är känd i förväg kan gå på bunten. Detta är fallet för siffror, strängar, booléer. Heapen är ett minne för objekt som du inte kan förutbestämma exakt storlek och struktur. Eftersom objekt och matriser kan muteras och förändras vid körning, måste de gå in i högen. Källa: Academind | CPU-stack och heap är fysiskt relaterade till hur CPU och register fungerar med minne, hur maskinmonteringsspråk fungerar, inte högnivåspråk själva, även om dessa språk kan avgöra små saker. Alla moderna processorer arbetar med "samma" mikroprocessorteori: de är alla baserade på vad som kallas "register" och vissa är för "stack" för att få prestanda. Alla processorer har stackregister sedan början och de hade alltid varit här, sätt att prata, som jag vet. Monteringsspråk är desamma sedan början, trots variationer ... upp till Microsoft och dess mellanliggande språk (IL) som ändrade paradigmet att ha ett OO-språk för montering av virtuella maskiner. Så vi kommer att kunna ha lite CLI / CIL CPU i framtiden (ett MS-projekt). Processorer har stackregister för att påskynda minnesåtkomst, men de är begränsade jämfört med användningen av andra register för att få full tillgång till allt tillgängligt minne för processus. Det var därför vi pratade om stack- och högtilldelningar. Sammanfattningsvis och i allmänhet är högen hudge och långsam och är för "globala" instanser och objektinnehåll, eftersom stacken är liten och snabb och för "lokala" variabler och referenser (dolda pekare att glömma att hantera dem). Så när vi använder det nya nyckelordet i en metod skapas referensen (en int) i stacken, men objektet och allt dess innehåll (värdetyper såväl som objekt) skapas i högen, om jag minns. Men lokala elementära värdetyper och matriser skapas i stacken. Skillnaden i minnesåtkomst är på cellernas referensnivå: adressering av högen, processens totala minne, kräver mer komplexitet när det gäller hantering av CPU-register, än stacken som är "mer" lokalt när det gäller adressering eftersom CPU-stacken registret används som basadress, om jag minns. Det är därför när vi har väldigt långa eller oändliga återkallande samtal eller slingor, vi fick stack överflöd snabbt utan att frysa systemet på moderna datorer ... C # Heap (ing) Vs Stack (ing) I .NET Stack vs Heap: Know the Difference Statisk klassminnesallokering där den lagras C # Vad och var är stacken och högen? https://en.wikipedia.org/wiki/Memory_management https://en.wikipedia.org/wiki/Stack_register Samlingsspråkresurser: Programmeringshandledning för montering Intel® 64 och IA-32 Architectures Software Developer Manuals | Tack för en riktigt bra diskussion men som en riktig noob undrar jag var instruktionerna finns? I BÖRJAN beslutade forskare mellan två arkitekturer (von NEUMANN där allt betraktas som DATA och HARVARD där ett minnesområde var reserverat för instruktioner och ett annat för data). I slutändan gick vi med von Neumann-designen och nu anses allt vara 'detsamma'. Detta gjorde det svårt för mig när jag lärde mig montering https://www.cs.virginia.edu/~evans/cs216/guides/x86.html eftersom de pratar om register och staplar pekare. Allt ovan talar om DATA. Min gissning är att eftersom en instruktion är en definierad sak med ett specifikt minnesavtryck, skulle den gå på stacken och så alla 'de' register som diskuteras vid montering finns på stacken. Naturligtvis kom objektorienterad programmering med instruktioner och data som kom in i en struktur som var dynamisk så nu skulle instruktioner också hållas på högen? | Mycket aktiv fråga. Tjäna 10 rykte för att svara på den här frågan. Kravet på rykte hjälper till att skydda denna fråga från skräppost och icke-svar-aktivitet. Inte svaret du letar efter? Bläddra bland andra frågor taggade minne-management stack språk-agnostisk heap dynamisk-minne-allokering eller ställa din egen fråga.